from base.util import *
from Quicker.qk_python import *
from Quicker.qk_comple import *


class CsvTest:

    def __init__(self):
        pass

    def csv_test(self, func, case_s: Iterator = None, convert_all=False, is_assert=False,
                 only_num=None, count=-1, case_CsvPath="",
                 list_e_to_str=False, is_txt=False, del_lastArg=False):
        case_s = list(self.read_yield(case_CsvPath, convert_all=convert_all,
                                      list_e_to_str=list_e_to_str, is_txt=is_txt)) if case_s is None else case_s
        only_num = len(case_s) if only_num and (len(case_s) <= only_num or only_num == -1) else only_num
        count = count if count > 0 else float("inf")
        _count = 0
        for num, line in enumerate(case_s, 1):
            line = line[:-1] if del_lastArg else line
            if only_num and only_num != num:
                continue
            _count += 1
            if _count > count:
                break
            if is_assert:
                *args, expect = line
                res = func(*args)
                assert res == expect, (f"\n[参数...]   [期望结果, 实际结果]"
                                       f"\n{args}   {[expect, res]}")
            else:
                # 判断参数类型选择执行
                if isinstance(line, tuple):
                    res = func(*line)  # line: list. 有多个参数就传多少个
                else:
                    res = func(line)
                # 判断返回值类型
                if isinstance(func, type):
                    res = res()
                elif isinstance(res, Generator):
                    res = list(res)
                print(f"{res}   {line}\n")

    def _convert(self, e, d: dict, is_force=False, list_e_to_str=False):
        if is_force:
            return eval(e) if not list_e_to_str else re.split(r" *, *", e.strip("[]"))
        for postfix, func in d.items():
            if e.endswith(postfix):
                return func(ustr(e).rreplace(postfix, "", 1))
        return e

    # _case_csv的参数顺序不能变, 因为和csv_test绑定了
    def read_yield(self, path="", quotechar="⏎", convert_all=False,
                   list_e_to_str=False, is_txt=False):
        # 类型转换: 根据结尾判断, 否则默认为字符串
        d = {
            ".li": lambda e: eval(e),  # .li 列表
            ".di": lambda e: eval(e),  # .int 数字
            ".int": lambda e: int(e),  # .di 字典
        }
        path = path or r"F:\A_RuanJian" \
                       r"\Py_project\pro_test\case_s.csv"
        # 类型转换: "[]"的是列表, "{}"是字典, "."结尾的是数字, 其他是字符串
        with open(path, encoding="utf-8") as fi:
            if is_txt:
                yield from ((e.strip(),) for e in
                            fi.read().split("-------"))
            else:
                fi = csv.reader(fi, **{'delimiter': "\t",
                                       'quotechar': quotechar})  # 斜杠作为包围符)
                # 符合的, 则去除相应后缀+执行函数. 否则.不转换
                yield from (
                    tuple(self._convert(e, d, convert_all, list_e_to_str) for e in line)
                    for line in fi if line and not line[0].startswith("#")  # 筛选: 去除空行、#开头的
                )


class qk_decorate(qk_decorate):
    """
    Quicker与python的一些问题总结/记录
    1. del \r
    2. "" to None. 默认值是None时, 需要把空字符串转None
    3. strip first. 有时候传入的值需要先strip
    4. 服务端的返回值必须是json支持的对象, 比如不能是ustr也不能是func
    """

    def __init__(self, to_strip: int = 0, del_empty: int = 1):
        super().__init__(to_strip, del_empty)
        self.del_empty = del_empty
        self.to_strip = to_strip


class udi(udi):
    """
    1.all value must be true(目的是为了条件判断时,方便用or and)
    2.if_func: then (用于条件判断)
        if first key si func, then all key same
    """

    def __init__(self, d):
        super().__init__(d)
        self.d = self.data
        assert all(self.d.values()), "all value must be true"
        self.key_is_func = False
        if callable(next(iter(self.d.keys()))):
            assert all(callable(k) for k in self.d.keys()), "if first key si func, then all key same"
            self.key_is_func = True

    def get(self, item, default=None):
        if self.key_is_func:
            for k, v in self.d.items():
                # noinspection PyCallingNonCallable
                if k(item): return v
            return default
        else:
            self.d.get(item, default)


if __name__ == '__main__':
    pass
    # _case_csv()
    # list(_case_csv())
